#ifndef BTL_EXTRA_GUI_IMAGEPAINTER
#define BTL_EXTRA_GUI_IMAGEPAINTER

#include <btl/Image/Image.hpp>

namespace btl
{
namespace extra
{
namespace gui
{

template <typename PixelType>
class ImagePainter
{

   public:

      ImagePainter(void);

      ImagePainter(ImageRegion<PixelType>& im);

      ~ImagePainter(void);

      inline void setImage(ImageRegion<PixelType>& im);

      ///Draw a line in the image using Bresenham's algorithm
      void drawLine(int xa, int ya, int xb, int yb, const PixelType& colour);

      inline void drawLine(const Eigen::Vector2i& a, const Eigen::Vector2i& b, const PixelType& colour);

      void drawCircle(int xc, int yc, int radius, const PixelType& colour);

      inline void drawCircle(const Eigen::Vector2i& c, int radius, const PixelType& colour);

      void drawFilledCircle(int xc, int yc, int radius, const PixelType& colour);
      void drawFilledCircle(const Eigen::Vector2i& c, int radius, const PixelType& colour);

   protected:

      void setPixel(int x, int y, const PixelType& colour);
      void fillRow(int xl, int xr, int y, const PixelType& colour);

      ImageRegion<PixelType> _image;

};

} //namespace gui
} //namespace extra
} //namespace btl

namespace btl
{
namespace extra
{

using gui::ImagePainter;

} //namespace extra
} //namespace btl


// ====================================================================
// === Implementation


namespace btl
{
namespace extra
{
namespace gui
{

template <typename PixelType>
ImagePainter<PixelType>::ImagePainter(void) {}

template <typename PixelType>
ImagePainter<PixelType>::ImagePainter(ImageRegion<PixelType>& im)
{
   setImage(im);
}

template <typename PixelType>
ImagePainter<PixelType>::~ImagePainter(void) {}

template <typename PixelType>
inline void ImagePainter<PixelType>::setImage(ImageRegion<PixelType>& im)
{
   _image = im;
}

template <typename PixelType>
void ImagePainter<PixelType>::drawLine(
   int xa, int ya,
   int xb, int yb,
   const PixelType& colour
)
{
   if(_image.null()) return;
   bool steep = abs(yb - ya) > abs(xb - xa);
   if(steep)
   {
      std::swap(xa, ya);
      std::swap(xb, yb);
   }
   if(xa > xb)
   {
      std::swap(xa, xb);
      std::swap(ya, yb);
   }
   int deltax = xb - xa;
   int deltay = abs(yb - ya);
   int error = deltax / 2;
   int ystep;
   if(ya < yb)
      ystep = 1;
   else
      ystep = -1;
   int y = ya;
   for(int x = xa; x < xb; ++x)
   {
      if(steep)
         setPixel(y, x, colour);
      else
         setPixel(x, y, colour);
      error -= deltay;
      if(error < 0)
      {
         y += ystep;
         error += deltax;
      }
   }
}

template <typename PixelType>
inline void ImagePainter<PixelType>::drawLine(
   const Eigen::Vector2i& a,
   const Eigen::Vector2i& b,
   const PixelType& colour
)
{
   drawLine(a(0), a(1), b(0), b(1), colour);
}

template <typename PixelType>
void ImagePainter<PixelType>::drawCircle(
   int xc, int yc,
   int radius,
   const PixelType& colour
)
{
   if(_image.null()) return;
   int f = 1 - radius;
   int ddF_x = 1;
   int ddF_y = -2 * radius;
   int x = 0;
   int y = radius;
   setPixel(xc, yc + radius, colour);
   setPixel(xc, yc - radius, colour);
   setPixel(xc + radius, yc, colour);
   setPixel(xc - radius, yc, colour);
   while(x < y)
   {
      if(f >= 0)
      {
         --y;
         ddF_y += 2;
         f += ddF_y;
      }
      ++x;
      ddF_x += 2;
      f += ddF_x;
      setPixel(xc + x, yc + y, colour);
      setPixel(xc - x, yc + y, colour);
      setPixel(xc + x, yc - y, colour);
      setPixel(xc - x, yc - y, colour);
      setPixel(xc + y, yc + x, colour);
      setPixel(xc - y, yc + x, colour);
      setPixel(xc + y, yc - x, colour);
      setPixel(xc - y, yc - x, colour);
   }
}

template <typename PixelType>
inline void ImagePainter<PixelType>::drawCircle(
   const Eigen::Vector2i& c,
   int radius,
   const PixelType& colour
)
{
   drawCircle(c(0), c(1), radius, colour);
}

template <typename PixelType>
void ImagePainter<PixelType>::drawFilledCircle(
   int xc, int yc,
   int radius,
   const PixelType& colour
)
{
   if(_image.null()) return;
   int f = 1 - radius;
   int ddF_x = 1;
   int ddF_y = -2 * radius;
   int x = 0;
   int y = radius;
   setPixel(xc, yc + radius, colour);
   setPixel(xc, yc - radius, colour);
   fillRow(xc - radius, xc + radius, yc, colour);
   while(x < y)
   {
      if(f >= 0)
      {
         --y;
         ddF_y += 2;
         f += ddF_y;
      }
      ++x;
      ddF_x += 2;
      f += ddF_x;
      fillRow(xc - x, xc + x, yc + y, colour);
      fillRow(xc - x, xc + x, yc - y, colour);
      fillRow(xc - y, xc + y, yc + x, colour);
      fillRow(xc - y, xc + y, yc - x, colour);
   }
}

template <typename PixelType>
inline void ImagePainter<PixelType>::drawFilledCircle(
   const Eigen::Vector2i& c,
   int radius,
   const PixelType& colour
)
{
   drawFilledCircle(c(0), c(1), radius, colour);
}

template <typename PixelType>
void ImagePainter<PixelType>::setPixel(int x, int y, const PixelType& colour)
{
   if((x >= 0) && (x < _image.width()) && (y >= 0) && (y < _image.height()))
      _image[y][x] = colour;
}

template <typename PixelType>
void ImagePainter<PixelType>::fillRow(int xl, int xr, int y, const PixelType& colour)
{
   assert(xl <= xr);
   if(xl < 0) xl = 0;
   if(xl > _image.width()) xl = _image.width()-1;
   if(xr < 0) xr = 0;
   if(xr > _image.width()) xr = _image.width()-1;
   if((y >= 0) && (y < _image.height()))
   {
      PixelType* row = _image[y];
      for(int i = xl; i <= xr; ++i)
         row[i] = colour;
   }
}

} //namespace gui
} //namespace extra
} //namespace btl


#endif //BTL_EXTRA_GUI_IMAGEPAINTER

